package com.quanz.ckgl.services.db;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.crank.crud.CriteriaUtils;
import org.crank.crud.criteria.Criterion;
import org.crank.crud.criteria.OrderBy;
import org.joda.time.DateTime;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;

import com.quanz.ckgl.services.Role;
import com.quanz.ckgl.services.User;
import com.quanz.ckgl.services.UserService;
import com.quanz.ckgl.services.dao.AuthoritiesDao;
import com.quanz.ckgl.services.dao.RoleDao;
import com.quanz.ckgl.services.dao.UserDao;
import com.quanz.ckgl.services.entity.AuthoritiesEntity;
import com.quanz.ckgl.services.entity.RoleEntity;
import com.quanz.ckgl.services.entity.UserEntity;

/**
 * 基于数据库的{@link UserService}实现
 * @author James Quan
 * @version 2015年10月5日 下午3:12:49
 */
public class DbUserService implements UserService {
	private UserDao userDao;
	private RoleDao roleDao;
	private AuthoritiesDao authoritiesDao;

	public UserDao getUserDao() {
		return userDao;
	}

	@Resource
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public RoleDao getRoleDao() {
		return roleDao;
	}

	@Resource
	public void setRoleDao(RoleDao roleDao) {
		this.roleDao = roleDao;
	}

	public AuthoritiesDao getAuthoritiesDao() {
		return authoritiesDao;
	}

	@Resource
	public void setAuthoritiesDao(AuthoritiesDao authoritiesDao) {
		this.authoritiesDao = authoritiesDao;
	}

	@Override
	@Transactional(readOnly = true)
	public User findUserById(long id) {
		UserEntity entity = userDao.findOne(id);
		return wrap(entity);
	}

	@Override
	@Transactional(readOnly = true)
	public User findUserByAccount(String account) {
		UserEntity entity = userDao.findByAccount(account);
		return wrap(entity);
	}

	@Override
	@Transactional(readOnly = true)
	public User[] findUser(OrderBy[] orderBy, int startPosition, int maxResults, Criterion... criteria) {
		Specification<UserEntity> spec = CriteriaUtils.getSpecification(UserEntity.class, criteria);
		Sort sort = CriteriaUtils.getSort(orderBy);
		List<UserEntity> entities = userDao.findList(spec, startPosition, maxResults, sort);
		List<User> users = new ArrayList<User>(entities.size());
		for (UserEntity entity : entities) {
			User user = wrap(entity);
			users.add(user);
		}
		return users.toArray(new User[users.size()]);
	}

	@Override
	@Transactional(readOnly = true)
	public int findUserCount(Criterion... criteria) {
		long count = userDao.count(CriteriaUtils.getSpecification(UserEntity.class, criteria));
		return (int) count;

	}

	@Override
	@Transactional(readOnly = true)
	public Role findRoleById(long id) {
		RoleEntity entity = roleDao.findOne(id);
		return wrap(entity);
	}

	@Override
	@Transactional(readOnly = true)
	public Role findRoleByCode(String code) {
		RoleEntity entity = roleDao.findByCode(code);
		return wrap(entity);
	}
	
	@Override
	@Transactional(readOnly = true)
	public Role[] findRole(OrderBy[] orderBy, int startPosition, int maxResults, Criterion... criteria) {
		Specification<RoleEntity> spec = CriteriaUtils.getSpecification(RoleEntity.class, criteria);
		Sort sort = CriteriaUtils.getSort(orderBy);
		List<RoleEntity> entities = roleDao.findList(spec, startPosition, maxResults, sort);
		List<Role> roles = new ArrayList<Role>(entities.size());
		for (RoleEntity entity : entities) {
			Role role = wrap(entity);
			roles.add(role);
		}
		return roles.toArray(new Role[roles.size()]);
	}
	
	@Override
	@Transactional(readOnly = true)
	public int findRoleCount(Criterion... criteria) {
		long count = roleDao.count(CriteriaUtils.getSpecification(RoleEntity.class, criteria));
		return (int) count;
	}

	@Override
	@Transactional(readOnly = true)
	public Role[] findUserRoles(User user) {
		UserEntity entity = userDao.findOne(user.getId());
		List<AuthoritiesEntity> list = entity == null ? null : authoritiesDao.findByUser(entity);
		if (list == null || list.isEmpty()) {
			return new Role[0];
		}
		Role[] roles = new Role[list.size()];
		for (int i = 0; i < list.size(); i++) {
			roles[i] = wrap(list.get(i).getRole());
		}
		return roles;
	}

	@Override
	public User[] findRoleMembers(Role role) {
		RoleEntity entity = roleDao.findOne(role.getId());
		List<AuthoritiesEntity> list = entity == null ? null : authoritiesDao.findByRole(entity);
		if (list == null || list.isEmpty()) {
			return new User[0];
		}
		User[] users = new User[list.size()];
		for (int i = 0; i < list.size(); i++) {
			users[i] = wrap(list.get(i).getUser());
		}
		return users;
	}

	@Override
	@Transactional
	public User createUser(User user) {
		if (findUserByAccount(user.getAccount()) != null) {
			throw new DuplicateKeyException("user " + user.getAccount() + " already existed!");
		}
		UserEntity entity = new UserEntity();
		entity.setAccount(user.getAccount());
		entity.setName(user.getName());
		entity.setPassword(user.getPassword());
		entity.setStatus(user.getStatus());
		DateTime createdTime = user.getCreatedTime() == null ? DateTime.now() : user.getCreatedTime();
		DateTime lastModified = user.getLastModified() == null ? createdTime : user.getLastModified();
		entity.setCreatedTime(createdTime);
		entity.setLastModified(lastModified);
		userDao.persist(entity);
		return wrap(entity);
	}

	@Override
	@Transactional
	public void removeUser(User user) {
		UserEntity entity = userDao.findOne(user.getId());
		if (entity == null) {
			return;
		}
		List<AuthoritiesEntity> list = authoritiesDao.findByUser(entity);
		if (list != null && !list.isEmpty()) {
			authoritiesDao.delete(list);
		}
		userDao.delete(entity);
	}

	@Override
	@Transactional
	public void updateUser(User user) {
		UserEntity entity = userDao.findOne(user.getId());
		if (entity == null) {
			throw new IllegalArgumentException("user " + user.getId() + " not found!");
		}
		entity.setName(user.getName());
		entity.setPassword(user.getPassword());
		entity.setStatus(user.getStatus());
		DateTime lastModified = user.getLastModified() == null ? DateTime.now() : user.getLastModified();
		entity.setLastModified(lastModified);
		entity = userDao.merge(entity);
	}

	@Override
	@Transactional
	public Role createRole(Role role) {
		if (findRoleByCode(role.getCode()) != null) {
			throw new DuplicateKeyException("role " + role.getCode() + " already existed!");
		}
		RoleEntity entity = new RoleEntity();
		entity.setCode(role.getCode());
		entity.setName(role.getName());
		DateTime createdTime = role.getCreatedTime() == null ? DateTime.now() : role.getCreatedTime();
		DateTime lastModified = role.getLastModified() == null ? createdTime : role.getLastModified();
		entity.setCreatedTime(createdTime);
		entity.setLastModified(lastModified);
		roleDao.persist(entity);
		return wrap(entity);
	}

	@Override
	@Transactional
	public void removeRole(Role role) {
		RoleEntity entity = roleDao.findOne(role.getId());
		if (entity == null) {
			return;
		}
		List<AuthoritiesEntity> list = authoritiesDao.findByRole(entity);
		if (list != null && !list.isEmpty()) {
			authoritiesDao.delete(list);
		}
		roleDao.delete(entity);
	}

	@Override
	@Transactional
	public void updateRole(Role role) {
		RoleEntity entity = roleDao.findOne(role.getId());
		if (entity == null) {
			throw new IllegalArgumentException("role " + role.getId() + " not found!");
		}
		entity.setName(role.getName());
		DateTime lastModified = role.getLastModified() == null ? DateTime.now() : role.getLastModified();
		entity.setLastModified(lastModified);
		entity = roleDao.merge(entity);
	}

	@Override
	@Transactional
	public void changeAuthority(User user, Role[] roles) {
		UserEntity userEntity = userDao.findOne(user.getId());
		if (userEntity == null) {
			throw new IllegalArgumentException("user " + user.getId() + " not found!");
		}
		List<AuthoritiesEntity> list = authoritiesDao.findByUser(userEntity);
		Map<Long, AuthoritiesEntity> toBeDeleted = new HashMap<Long, AuthoritiesEntity>();
		for (AuthoritiesEntity entity : list) {
			toBeDeleted.put(entity.getRole().getId(), entity);
		}
		Map<Long, AuthoritiesEntity> noChange = new HashMap<Long, AuthoritiesEntity>();
		Map<Long, AuthoritiesEntity> toBeAdd = new HashMap<Long, AuthoritiesEntity>();
		for (Role role : roles) {
			long id = role.getId();
			if (noChange.containsKey(id) || toBeAdd.containsKey(id)) {
				continue;
			}
			AuthoritiesEntity entity = toBeDeleted.remove(id);
			if (entity != null) {
				noChange.put(id, entity);
				continue;
			} else {
				RoleEntity roleEntity = roleDao.findOne(id);
				if (roleEntity == null) {
					throw new IllegalArgumentException("role " + id + " not found!");
				} else {
					AuthoritiesEntity authEntity = new AuthoritiesEntity();
					authEntity.setUser(userEntity);
					authEntity.setRole(roleEntity);
					toBeAdd.put(id, authEntity);
				}
			}
		}
		if (!toBeDeleted.isEmpty()) {
			authoritiesDao.delete(toBeDeleted.values());
		}
		if (!toBeAdd.isEmpty()) {
			authoritiesDao.persist(toBeAdd.values());
		}
	}

	protected User wrap(UserEntity entity) {
		if (entity == null) {
			return null;
		}
		User user = new User();
		user.setId(entity.getId());
		user.setAccount(entity.getAccount());
		user.setName(entity.getName());
		user.setPassword(entity.getPassword());
		user.setStatus(entity.getStatus());
		user.setCreatedTime(entity.getCreatedTime());
		user.setLastModified(entity.getLastModified());
		return user;
	}

	protected Role wrap(RoleEntity entity) {
		if (entity == null) {
			return null;
		}
		Role role = new Role();
		role.setId(entity.getId());
		role.setCode(entity.getCode());
		role.setName(entity.getName());
		role.setCreatedTime(entity.getCreatedTime());
		role.setLastModified(entity.getLastModified());
		return role;
	}
}
